Github
Postsetccheckmate hotfix

checkmate hotfix

A set of two-state buttons that can be toggled on or off

출석 및 출결관리 서비스인 체크메이트를 배포하고 몇가지 긴급하게 수정해야하는 문제가 발생했다.

첫번째로 모바일(아이폰) 환경에서 위아래 네비게이션 바가 보이지 않는 버그가 있었다.
두번째로 ATK가 만료되었을 때, 401 상태코드에 대한 에러핸들링이 되지 않아서 그대로 에러페이지를 보여주는 버그가 있었다.

위 버그를 develop 브랜치에서 작업을 해서 merge를 하기에는 이미 개발 중인 기능들이 올라가 있었기 때문에 불가능했다. 각 버그를 차례대로 hotfix 브랜치에서 해결하기로 결정했다.

체크메이트에서 hotfix를 하는 전체 과정은 일반 기능 개발과정과 같다.

hotfix이지만 또 다른 실수를 방지하기 위해서 리뷰를 진행하고 Approve가 되었을 시 운영 브랜치에 반영한다.

hotfix를 진행하는 branching 과정은 다음과 같다.

레이아웃 버그 hotfix

문제 상황은 상단과 하단 네비게이션 바가 보이지 않는 버그였습니다. 네비게이션 바를 통해서 출석체크 페이지와 설정 페이지로 이동할 수 있었기 때문에 서비스 사용에 큰 불편을 주고 있어서 빠른 수정이 필요했습니다.

문제 원인은 모바일 브라우저에서 100vh는 상단 주소창 영역과 하단 네비게이션 영역의 사이즈를 포함하기 때문이었습니다. 네비게이션 바는 fixed position으로 inset을 이용해서 위치와 크기를 지정했었습니다. height를 100vh에서 원하는 크기만큼 빼서 지정하고 있었습니다. 모바일 브라우저에서는 주소창의 유무가 스크롤을 통해서 변경이 되었기 때문에 주소창이 나타나게 되면 bottom은 고정되어 있지만 top의 기준이 아래 방향으로 내려가면서 네비게이션 바가 밀려나는 문제였습니다.

image

문제를 해결하는 방법으로 2가지를 고려했습니다.

첫 번째 방법은 javascript의 window.innerHeight를 이용하는 것이었습니다. innerHeight는 창의 레이아웃 뷰 포트의 높이 값으로 주소창을 제외한 크기이기 때문에 사용하기 적절할 것이라고 예상했습니다. 자바스트립트를 사용해서 Element style에 새로운 단위의 vh를 프로퍼티로 설정해주는 방식으로 시간은 더 소요되지만 앞으로 사용하기 편리할 것이라고 생각했습니다. 단점으로 해당 자바스크립트 구문이 실행되기 전까지는 적용되지 않는다는 점이 있었습니다.

두 번째는 inset 대신에 top 또는 bottom, left, right와 height를 따로 지정하는 방법이었습니다. 가장 확실한 방법으로 빠른 시간내에 버그를 해결할 수 있었습니다.

결론은 두 번째 방법으로 문제를 해결하기로 결정했습니다. 이유는 빠르게 해결하는 것이 우선이었고, 첫 번째 방법은 단점이 존재하며 새로운 프로퍼티를 추가하는 방식이 추후에 혼란을 줄 수 있다고 판단했기 때문입니다.

이 문제를 해결하면서 아쉽다고 생각했던 점은 모바일 타겟 서비스임에도 테스크톱 환경에서만 확인했던 것입니다. 이후부터는 개발단계부터 실제 모바일에서 어떻게 렌더링되고 있는지 필수로 테스트하도록 했습니다.

401 에러 핸들링

문제 상황은 Access 토큰이 만료되었을 때 에러페이지를 보여주는 것이었습니다. 예상했던 흐름은 만료되었다면 저장된 Access 토큰을 삭제하고 로그아웃한 다음 로그인 페이지로 이동하는 것이었습니다. 하지만 에러 페이지만 렌더링되고 있었습니다. 문제의 원인은 실패했을 때의 에러 핸들링 로직이 포함되어 있지 않아서 사용자의 정보와 localStorage에 저장된 Access 토큰을 삭제하지 못했기 때문이었습니다.

문제를 해결하기 위해서 유효하지 않은 토큰 응답을 받았을 때 처리하는 로직을 추가했습니다. 하지만 작성된 권한이 필요한 요청마다 로직을 추가하기에는 시간적인 비용이 많이 소모되며, 중복되는 로직이 생길거라 판단했습니다. 이 문제를 해결하기 위해서 http 요청 후에 공통적으로 처리하는 Interceptor를 구현했습니다. Interceptor는 요청 후로 실행할 메소드를 가지는 객체로서 리액트 외부에서 정의했고, fetch 후에 실패했다면 해당 메소드를 실행시키도록 했습니다.

추가로 마주쳤던 문제는 공통적으로 실행해야 했던 동작이 리액트 상태로 저장된 Access 토큰과 사용자의 정보를 사용하는 함수였기 때문에 리액트 외부에서 사전에 정의해놓을 수 없는 것이었습니다.

해결 방식

Interceptor를 추가하여 응답 성공, 실패에 따른 특정한 동작을 실행하도록 했습니다. 유효하지 않은 토큰 응답에 대해서 공통적인 처리가 필요했습니다. 상태를 업데이트하는 useInterceptor라는 hook을 구현했습니다. 최상위 컴포넌트인 App이 마운트되었을 때, 객체로 주입하도록 했습니다. useInterceptor에는 요청 후 실행할 메소드를 가지는 객체를 인자로 전달받아 최초 한 번 동작하도록 했습니다

이후에 Interceptor를 요청 전에도 공통적인 동작을 실행할 수 있도록 개선했습니다. http 메세지 header에 Access 토큰을 추가하는 기능을 추가했고, 중복되는 코드와 관리 포인트를 줄일 수 있었습니다.